package main

import (
	"context"
	"fmt"
	"time"

	"go.etcd.io/etcd/mvcc/mvccpb"

	"go.etcd.io/etcd/clientv3"
)

func main() {
	var (
		config             clientv3.Config
		client             *clientv3.Client
		err                error
		kv                 clientv3.KV
		watcher            clientv3.Watcher
		getResp            *clientv3.GetResponse
		watchStartRevision int64
		watchRespChan      <-chan clientv3.WatchResponse
		watchResp          clientv3.WatchResponse
		event              *clientv3.Event
	)

	// 客户端配置
	config = clientv3.Config{
		Endpoints:   []string{"127.0.0.1:2379"}, // 集群列表
		DialTimeout: 5 * time.Second,
	}

	// 建立一个客户端
	if client, err = clientv3.New(config); err != nil {
		fmt.Println(err)
		return
	}

	// KV：读写etcd的键值对
	kv = clientv3.NewKV(client)

	// 模拟etcd中KV的变化
	go func() {
		for {
			kv.Put(context.TODO(), "/cron/jobs/job7", "i am job7")

			kv.Delete(context.TODO(), "/cron/jobs/job7")

			time.Sleep(1 * time.Second)
		}
	}()

	// 先GET到当前的值，并监听后续变化
	if getResp, err = kv.Get(context.TODO(), "/cron/jobs/job7"); err != nil {
		fmt.Println(err)
		return
	}

	// key存在，长度不为0
	if len(getResp.Kvs) != 0 {
		fmt.Println("当前值：", string(getResp.Kvs[0].Value))
	}

	// 当前etcd集群事务ID的下一个为起点开始监听，单调递增 (etcd修改操作，都会有一个事务唯一标识)
	watchStartRevision = getResp.Header.Revision + 1

	// 创建一个watcher监听器
	watcher = clientv3.NewWatcher(client)

	// 启动监听
	fmt.Println("从该版本向后监听：", watchStartRevision)

	// 通过操作context，5秒后退出监听
	ctx, cancelFunc := context.WithCancel(context.TODO())
	time.AfterFunc(5*time.Second, func() {
		cancelFunc()
	})

	// WithRev：指定版本
	watchRespChan = watcher.Watch(ctx, "/cron/jobs/job7", clientv3.WithRev(watchStartRevision))

	// 通过context可以取消watcher监听，下面的watchRespChan就会被close，for循环就会退出

	// 处理Kv变化事件
	for watchResp = range watchRespChan {
		for _, event = range watchResp.Events { // 打包推送过来的，Events中可能有不同的变化
			switch event.Type {
			case mvccpb.PUT:
				// CreateRevision：创建版本 ModRevision：修改版本
				fmt.Println("修改为：", string(event.Kv.Value), "Revision：", event.Kv.CreateRevision, event.Kv.ModRevision)
			case mvccpb.DELETE:
				fmt.Println("删除了：", event.Kv.ModRevision)
			}
		}
	}
}
